package com.eunion.web.common.base;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import org.apache.commons.lang3.StringUtils;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.http.MediaType;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import com.eunion.core.DateJsonProcessor;
import com.eunion.core.LogUtil;
import com.eunion.core.PageConditions;
import com.eunion.core.PageData;
import com.eunion.core.common.DeleteVo;
import com.eunion.core.common.JsonVo;
import com.eunion.core.exception.ValidateException;
import com.eunion.web.common.base.search.SearchOperator;
import com.eunion.web.common.vo.Web;
import com.eunion.web.common.vo.WebPageVo;
import com.eunion.web.common.vo.WebStatus;

import net.sf.json.JSONArray;
import net.sf.json.JsonConfig;

/**
 * 基类的实现，提供一些基础方法. <br> 主要为子类提供通用的方法处理
 * @param <T>
 * @param <ID>
 * @version 2015-9-13
 */
public abstract class BaseCommonController<T, ID extends Serializable> extends BaseAbstractController<T, ID> {

    /** 所属的module , module一般为Controller类名的去掉controller后的小写. */
    private String module = this.getClass().getSimpleName().toLowerCase().replaceAll("controller", "");

    /** urlBase 一般为module/module . */
    private String urlBase = getModule().concat(Web.URL_SEPARATOR).concat(getModule());

    /**
     * 获取当前请求request.
     */
    protected HttpServletRequest getRequest() {
        return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
    }

    /**
     * 获取当前请求session.
     */
    protected HttpSession getSession() {
        return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest().getSession();
    }

    @ModelAttribute
    public void populateModel(Model model) {
        model.addAttribute("reqUrl",getRequest().getRequestURI());
    }

    /**
     * 检查bindingresult中是否有异常.
     * @throws ValidateException 验证的异常.
     */
    protected void checkBindResult(BindingResult result) {
        // ObjectValidator.validate(obj); //使用这句配置catch模块也可以完成验证
        if (result.hasErrors()) {
            List<ObjectError> list = result.getAllErrors();
            for (ObjectError error : list) {
                if (StringUtils.isNotEmpty(error.getDefaultMessage())) {
                    throw new ValidateException(error.getDefaultMessage());
                }
            }
        }
    }

    /**
     * 获取模块名.
     * @return 模块名
     */
    protected String getModule() {
        return module;
    }

    /**
     * 获取菜单中文名.
     * @return 菜单名
     */
    protected String getMenu() {
        return module;
    }

    /**
     * get 时 跳转到首页页面.
     * @param obj 实体对象
     * @param model a holder for model attributes 数据
     * @return 首页页面
     */
    @RequestMapping(value = "/save", method = {RequestMethod.GET})
    protected String saveGet(T obj, Model model) {
        return main(null, model);
    }

    /**
     * get 时 跳转到首页页面.
     * @param obj 实体对象
     * @param model a holder for model attributes 数据
     * @return 首页页面
     */
    @RequestMapping(value = "/update", method = {RequestMethod.GET})
    protected String updateGet(T obj, Model model) {
        return main(null, model);
    }

    /**
     * 跳转到首页面.
     * @param model a holder for model attributes 数据
     * @return 首页页面
     */
    @RequestMapping(value = {Web.URL_SEP_INDEX}, method = RequestMethod.GET)
    protected String index(Model model) {
        return main(null, model);
    }

    /**
     * 跳转到首页面.
     * @param model a holder for model attributes 数据
     * @return 首页页面
     */
    @RequestMapping(method = {RequestMethod.GET, RequestMethod.POST})
    protected String main(PageData<T> pages, Model model) {
        setModuleQuery(model);
        // model.addAttribute(Web.MENU, getWebPageVo());
        return getUrlBase();
    }

    /**
     * 跳转到更新页面.
     * @param id 实体对象的主键
     * @param model a holder for model attributes 数据
     * @return 查看页面
     */
    @RequestMapping(value = Web.URL_SEP_VIEW + "/{id}", method = RequestMethod.GET)
    protected String view(@PathVariable ID id, Model model) {
        // setModule(model);
        setModuleView(model);
        T objDb = getBaseService().findOne(id);
        model.addAttribute(Web.OBJ, objDb);
        return getUrlBase().concat("-view");
    }

    /**
     * 跳转到新增页面.
     * @param model a holder for model attributes 数据
     * @return 新增页面
     */
    @RequestMapping(value = Web.URL_SEP_ADD, method = RequestMethod.GET)
    protected String add(Model model) {
        // setModule(model);
        setModuleAdd(model);
        model.addAttribute(Web.OBJ, getT());
        return getUrlBase().concat("-add");
    }

    /**
     * 跳转到导入页面.
     * @param model a holder for model attributes 数据
     * @return 导入页面
     */
    @RequestMapping(value = Web.URL_SEP_IMPORT, method = RequestMethod.GET)
    protected String imports(Model model) {
        setModuleImport(model);
        model.addAttribute(Web.OBJ, getT());
        return getUrlBase().concat("-import");
    }
    
    /**
     * 跳转到更新页面.
     * @param id 待更新的对象ID
     * @param model a holder for model attributes 数据
     * @return 更新页面
     */
    @RequestMapping(value = Web.URL_SEP_EDIT + "/{id}", method = RequestMethod.GET)
    protected String edit(@PathVariable ID id, Model model) {
        setModuleEdit(model);
        T objDb = getBaseService().findOne(id);
        model.addAttribute(Web.OBJ, objDb);
        return getUrlBase().concat("-edit");
    }

    /**
     * 跳转到更新页面.
     * @param id 待更新的对象ID
     * @param model a holder for model attributes 数据
     * @return 更新页面
     */
    @RequestMapping(value = Web.URL_SEP_BROWSE + "/{id}", method = RequestMethod.GET)
    protected String browse(@PathVariable ID id, Model model) {
        setModuleEdit(model);
        T objDb = getBaseService().findOne(id);
        model.addAttribute(Web.OBJ, objDb);
        return getUrlBase().concat("-browse");
    }

    /**
     * 根据传入的数据ids,来删除对象.
     */
    @RequestMapping(value = Web.URL_SEP_REMOVE, method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
    @ResponseBody
    protected JsonVo remove(@RequestBody DeleteVo<ID> dvo) {
        try {
            if (dvo != null && dvo.getIds() != null && dvo.getIds().size() > 0) {
                // 2016-01-10 这里肯定要修改为批删除的，而且要重写Responsitory里的内容？
                List<T> tts = getBaseService().findAll(dvo.getIds());
                if (tts != null && !tts.isEmpty()) {
                    getBaseService().delete(tts);
                }
            }
            return new JsonVo(true);
        } catch (Exception e) {
            LogUtil.get(this.getClass()).error("remove erro ", e);
            return new JsonVo(false, "删除出现异常");
        }
    }

    /**
     * 分页ajax数据请求.
     * @param params 请求参数 Caused by: org.springframework.http.InvalidMediaTypeException: Invalid mime type
     *            "application/json;content-type=application/json": Invalid token character '/' in token "application/json"
     * @return 返回分页数据 content-type=application/json,application/xml,application/x-www-form-urlencoded ,headers ={"Accept=application/json"}
     */
    @RequestMapping(value = "/page.json", method = {RequestMethod.POST}, produces = MediaType.APPLICATION_JSON_VALUE)
    @ResponseBody
    protected PageData<T> page(@RequestBody PageData<T> params) {
        if (null == params) {
            params = new PageData<T>();
            return params;
        }
        // 分页参数合法性校验
        params.calculate();
        Integer size = params.getPageSize();
        if (null == size || size < PageData.PAGE_SIZE_DEFAULT) {
            size = PageData.PAGE_SIZE_DEFAULT;
        }

        // PageRequest页面从0开始
        int pageNumber = params.getPageNumber() - 1;
        Pageable pageAble = new PageRequest(pageNumber, size);
        Page<T> page = getBaseService().findAll(getCondition(params), pageAble);
        params.setTotal(page.getTotalElements());
        params.setRows(page.getContent());
        params.setRowData(getRowDataJson(page.getContent()));
        // 设置总页数
        params.setPageCount(page.getTotalPages());
        return params;
    }

    /**
     * 根据前台头面生成查询条件.
     * @param params 参数
     * @return 返回查询条件
     */
    protected Specification<T> getCondition(final PageData<T> params) {
        return new Specification<T>() {

            private Predicate getCriteriaBuilder(CriteriaBuilder cb, Root<T> root, PageConditions pc) {
                Predicate pr = null;
                // 确保数据有效
                if (pc != null && StringUtils.isNotEmpty(pc.getKey()) && null != pc.getType() && null != pc.getValue()) {
                    if (pc.getOperator().equalsIgnoreCase(SearchOperator.Conditon.eq)) {
                        pr = cb.equal(root.get(pc.getKey()), pc.getValue());
                    } else if (pc.getOperator().equalsIgnoreCase(SearchOperator.Conditon.like)) {
                        if (pc.getType().equalsIgnoreCase(SearchOperator.Type.string)) {
                            pr = cb.like(root.<String> get(pc.getKey()), "%" + pc.getValue() + "%");
                        }
                    } else if (pc.getOperator().equalsIgnoreCase(SearchOperator.Conditon.ge)) {
                        if (pc.getType().equalsIgnoreCase(SearchOperator.Type.string)) {
                            pr = cb.greaterThanOrEqualTo(root.<String> get(pc.getKey()), (String) pc.getValue());
                        } else if (pc.getType().equalsIgnoreCase(SearchOperator.Type.intger)) {
                            pr = cb.greaterThanOrEqualTo(root.<Integer> get(pc.getKey()), Integer.valueOf(pc.getValue().toString()));
                        } else if (pc.getType().equalsIgnoreCase(SearchOperator.Type.LONG)) {
                            pr = cb.greaterThanOrEqualTo(root.<Long> get(pc.getKey()), Long.valueOf(pc.getValue().toString()));
                        }
                    } else if (pc.getOperator().equalsIgnoreCase(SearchOperator.Conditon.le)) {
                        if (pc.getType().equalsIgnoreCase(SearchOperator.Type.string)) {
                            pr = cb.lessThanOrEqualTo(root.<String> get(pc.getKey()), (String) pc.getValue());
                        } else if (pc.getType().equalsIgnoreCase(SearchOperator.Type.intger)) {
                            pr = cb.lessThanOrEqualTo(root.<Integer> get(pc.getKey()), Integer.valueOf(pc.getValue().toString()));
                        } else if (pc.getType().equalsIgnoreCase(SearchOperator.Type.LONG)) {
                            pr = cb.lessThanOrEqualTo(root.<Long> get(pc.getKey()), Long.valueOf(pc.getValue().toString()));
                        }
                    } 

                }
                return pr;
            }

            @Override
            public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
                List<Predicate> list = new ArrayList<Predicate>();
                List<PageConditions> conditions = params.getConditions();
                if (conditions != null && conditions.size() > 0) {
                    for (PageConditions pc : conditions) {
                        // LOG.debug("search PageConditions is :{}", pc);
                        Predicate pr = getCriteriaBuilder(cb, root, pc);
                        if (pr != null) {
                            // LOG.debug("search PageConditions is added:{}", pc);
                            list.add(pr);
                        }
                    }
                }

                Predicate[] p = new Predicate[list.size()];
                query.where(cb.and(list.toArray(p)));
                setOrder(root, query, cb, params);
                return query.getRestriction();
            }
        };
    }

    /**
     * 设置默认排序 .
     * @param root Root
     * @param query CriteriaQuery
     * @param cb CriteriaBuilder
     * @param params PageParams参数
     */
    protected void setOrder(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb, PageData<T> params) {
        if (StringUtils.isNotBlank(params.getSort())) {
            if (params.isDesc()) {
                query.orderBy(cb.desc(root.get(params.getSort().trim())));
            } else {
                query.orderBy(cb.asc(root.get(params.getSort().trim())));
            }
        }
    }

    /**
     * 设置 MENU 查询.
     * @param model a holder for model attributes 为设置如的对象
     */
    protected void setModuleQuery(Model model) {
        setModule(model, Web.MENU, new WebPageVo().setMenuEn(getModule()).setMenu(getMenu()).setAction("查询"));
    }

    /**
     * 设置 STATUS OK 200.
     * @param model a holder for model attributes
     */
    protected void setModuleStatusOK(Model model) {
        model.addAttribute(Web.STATUS, new WebStatus());
    }

    /**
     * 设置 STATUS OK 200.
     * @param model a holder for model attributes
     */
    protected void setModuleStatusOK(Model model, String msseage) {
        model.addAttribute(Web.STATUS, new WebStatus().setCodeOK(msseage));
    }

    /**
     * 设置 STATUS 500.
     * @param model a holder for model attributes
     * @param errorMessage 错误消息
     */
    protected void setModuleStatusError(Model model, String errorMessage) {
        model.addAttribute(Web.STATUS, new WebStatus().setCodeError(errorMessage));
    }

    /**
     * 设置 STATUS 自定义status.
     * @param model a holder for model attributes
     * @param status 状态吗 ?
     */
    protected void setModuleStatus(Model model, WebStatus status) {
        model.addAttribute(Web.STATUS, status);
    }

    /**
     * 设置 MENU 查看.
     * @param model a holder for model attributes
     */
    protected void setModuleView(Model model) {
        setModule(model, Web.MENU, new WebPageVo().setMenuEn(getModule()).setMenu(getMenu()).setAction("查看"));
    }

    /**
     * 设置 MENU 新增.
     * @param model a holder for model attributes
     */
    protected void setModuleAdd(Model model) {
        setModule(model, Web.MENU, new WebPageVo().setMenuEn(getModule()).setMenu(getMenu()).setAction("新增"));
    }

    /**
     * 设置 MENU 修改.
     * @param model a holder for model attributes
     */
    protected void setModuleEdit(Model model) {
        setModule(model, Web.MENU, new WebPageVo().setMenuEn(getModule()).setMenu(getMenu()).setAction("修改"));
    }

    /**
     * 设置 MENU 导入.
     * @param model a holder for model attributes
     */
    protected void setModuleImport(Model model) {
        setModule(model, Web.MENU, new WebPageVo().setMenuEn(getModule()).setMenu(getMenu()).setAction("导入"));
    }
    
    /**
     * 设置所属模块信息到MENU对象.
     * @param model a holder for model attributes
     */
    protected void setModule(Model model) {
        WebPageVo menuVo = getWebPageVo();
        model.addAttribute(Web.MENU, menuVo);
    }

    /**
     * 设置所属模块信息到MENU对象.
     * @param model a holder for model attributes
     * @param key 待设置的key
     * @param menuVo menoVo
     */
    protected void setModule(Model model, String key, WebPageVo menuVo) {
        model.addAttribute(key, menuVo);
    }

    /**
     * 获取urlbase值. default is getModule().concat(Web.URL_SEPARATOR).concat(getModule()).
     * @return urlbase值
     */
    protected String getUrlBase() {
        return urlBase;
    }

    @Override
    protected WebPageVo getWebPageVo() {
        WebPageVo vo = getWebPageVoSUb();
        if (vo == null) {
            vo = new WebPageVo();
        }

        if (StringUtils.isEmpty(vo.getMenu())) {
            vo.setMenu(getModule());
        }
        return vo;
    }

    /**
     * List数据转换成json.
     */
    protected String getRowDataJson(List<T> list) {
        JSONArray json = JSONArray.fromObject(list, getJsonConfig());
        String jsons = json.toString();
        return jsons;
    }

    /**
     * Json对象在转换的过程中会出现死循环及类型的转换,这个定制需要下放到子类.
     * @return
     */
    protected JsonConfig getJsonConfig() {
        JsonConfig cfg = new JsonConfig();
        // net.sf.json.JSONException: java.lang.reflect.InvocationTargetException异常
        // cfg.setExcludes(new String[]{"handler","hibernateLazyInitializer"});
        //
        // //过滤关联，避免死循环net.sf.json.JSONException: java.lang.reflect.InvocationTargetException
        // cfg.setJsonPropertyFilter(new PropertyFilter() {
        // public boolean apply(Object source, String name, Object value) {
        // if(name.equals("addressGroup")||name.equals("user")||name.equals("createTime")||name.equals("birthday")) {
        // return true;
        // } else {
        // return false;
        // }
        // }
        // });
        //
        // //javabean里出现循环调用啦，赶快用excludes干掉parent或者children
        // cfg.setExcludes(new String[]{"sysUri","sysUris","sysRoleUriLinks"});
        // net.sf.json.JSONException: java.lang.reflect.InvocationTargetException日期格式转化出错
        // cfg.setCycleDetectionStrategy(CycleDetectionStrategy.LENIENT);
        cfg.registerJsonValueProcessor(Date.class, new DateJsonProcessor("yyyy-MM-dd HH:mm:ss"));
        return cfg;
    }

    /**
     * 获取WebPageVo子對象.
     */
    protected WebPageVo getWebPageVoSUb() {
        return null;
    }

    public void setModule(String module) {
        this.module = module;
    }

    public void setUrlBase(String urlBase) {
        this.urlBase = urlBase;
    }

}
